use std::mem;

// this function borrows a slice
fn analyze_slice(slice: &[i32]) {
    
    println!("First element of the slice:{}", slice[0]);
    println!("the slice has {} elements", slice.len());
}

fn main() {
    // Fixed-size array (type signature is superfluous).
    let xs: [i32; 5] = [1, 2, 3, 4, 5];

    // all elements can be initialized to the same value.
    let ys: [i32; 500] = [0; 500];

    // Indexing starts at 0
    // it's better to distinguish between arrays and tuples when accessing
    println!("First element of the array: {}", xs[0]);
    println!("Second element of the array: {}", xs[1]);

    // `len` returns the count of elements in the array.
    println!("Number of elements in the array: {}", xs.len());

    // Arrays are stack allocated.
    println!("Array occupies {} bytes", mem::size_of_val(&xs));

    // Arrays can be automatically borrowed as slices.
    println!("Borrow the whole array as a slice.");
    analyze_slice(&xs);

    // Slices can point to a section of an array.
    // the are of the form [starting_index..ending_index].
    // The starting index is included, but the ending index is not.
    println!("Borrow a section of the array as a slice.");
    analyze_slice(&ys[1 .. 4]);
    // if we use negative indexing, there will be a compile time error:
    // the trait bound `usize: Neg` is not satisfied
    // analyze_slice(&ys[1..-1]);

    // if end indexing is less than start indexing, there will be a runtime time error:
    // slice index starts at 2 but ends at 1
    // analyze_slice(&ys[2..1]);

    // example of empty slice `&[]`:
    let empty_array: [u32; 0] = [];
    assert_eq!(&empty_array, &[]);
    assert_eq!(&empty_array, &[][..]); // Same but more verbose

    // Arrays can be safely accessed using `.get`, which returns an
    // `Option`. this can be matched as shown below, or used with
    // `.expect()` if you would lick the program to exit with a nice
    // message instead of happily continue.
    for i in 0..xs.len() + 1 {
        match xs.get(i) {
            Some(xval) => println!("{}: {}", i, xval),
            None => println!("{} is out of bounds!", i),
        }
    }

    // out of bounds indexing on array causes runtime time error.
    // index out of bounds: the len is 5 but the index is 5
    // println!("{}", xs[5]);
    // out of bounds indexing on slice causes runtime error.
    // index out of bounds: the len is 5 but the index is 5
    // println!("{}", xs[..][5]);
}